#pragma once

#include "ArFileEnumerator.hpp"
#include "ArPath.hpp"
#include "ArThumbView.hpp"
#include "CGThumbnail.h"

struct CGThumbEntry : virtual Aria::FileInfo
{
	static bool Compare(const CGThumbEntry &left, const CGThumbEntry &right)
	{
		bool leftIsDir = (left.m_fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
		bool rightIsDir = (right.m_fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
		if (leftIsDir != rightIsDir)
		{
			if (leftIsDir)
				return true;
			else
				return false;
		}
		else
			return (left.m_fileName.CompareNoCase(right.m_fileName) < 0);
	}

	CGThumbEntry(const Aria::FileInfo &other)
		: Aria::FileInfo(other)
	{		
		SHFILEINFO fileInfo;
		SHGetFileInfo(m_fileName, m_fileAttributes, &fileInfo, sizeof(fileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SYSICONINDEX);
		m_iconIndex = fileInfo.iIcon;

		m_locked = false;
		m_imageLoaded = false;
		m_image = NULL;
	}

	~CGThumbEntry(void)
	{
		if (m_image != NULL)
		{
			delete m_image;
			m_image = NULL;
		}
	}

	bool volatile m_locked;
	int m_iconIndex;
	bool m_imageLoaded;
	Aria::Image *m_image;
};

class CGThumbView : public Aria::ThumbViewImpl<CGThumbView, CGThumbEntry>
{
public:
	CGThumbView(void)
	{
		IImageList *obj;
		SHGetImageList(SHIL_SMALL, IID_IImageList, (void**)&obj);
		m_imageList = IImageListToHIMAGELIST(obj);
	}

	~CGThumbView(void)
	{
		EndUpdateImages();
	}
	
	void DrawIcon(CGThumbEntry &entry, CDCHandle dc, const CRect &rect)
	{
		ImageList_Draw(m_imageList, entry.m_iconIndex, dc, rect.left, rect.top, ILD_NORMAL | ILD_TRANSPARENT);
	}

	void DrawImage(CGThumbEntry &entry, CDCHandle dc, const CRect &rect)
	{
		if (entry.m_locked)
			return;
		
		if (!entry.m_imageLoaded)
			UpdateImage(entry, GetImageSize());
			//BeginUpdateImage(entry);
		else if (entry.m_image != NULL)
		{
			CRect dstRect = CGRect::Center(rect, CRect(CPoint(0, 0), CSize(entry.m_image->GetWidth(), entry.m_image->GetHeight())));
			entry.m_image->Draw(dc, dstRect.left, dstRect.top);
		}
	}

	const Aria::String& GetCaption(CGThumbEntry &entry)
	{
		return entry.m_fileName;	
	}

	CGThumbEntry& GetEntry(Aria::Int32 index)
	{
		return m_entries[index];
	}

	Aria::Int32 GetEntryCount(void)
	{
		return m_entries.size();
	}

	void OpenEntry(Aria::Int32 index)
	{
		CGThumbEntry &entry = m_entries[index];
		if (entry.m_fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			Aria::String newPath = Aria::Path::Combine(m_location, entry.m_fileName);
			SetLocation(newPath);
		}
	}

	bool SetLocation(const CString &newLocation)
	{
		EndUpdateImages();
		m_entries.clear();
		
		Aria::String newPath = Aria::Path::Normalize(newLocation);
		if (!Aria::Path::IsDirectoryExists(newPath))
			return false;

		Aria::FileEnumerator fileEnum(newPath, L"*.*", false);
		Aria::FileInfo fileInfo;
		while (fileEnum.Next(fileInfo))
		{
			CGThumbEntry *entry = new CGThumbEntry(fileInfo);
			m_entries.push_back(entry);
		}
		
		sort(m_entries.begin(), m_entries.end(), bind(&CGThumbEntry::Compare, _1, _2));
		PostMessage(WM_LOCATIONCHANGED, 0, 0);

		m_location = newPath;
		return true;
	}
private:
	void BeginUpdateImage(CGThumbEntry &entry)
	{
		m_threads.add_thread(new thread(bind(&CGThumbView::UpdateImage, this, ref(entry), GetImageSize())));
	}

	void EndUpdateImages(void)
	{
		m_threads.join_all();
	}

	void UpdateImage(CGThumbEntry &entry, CSize maxSize)
	{
		entry.m_locked = true;
		if (entry.m_image != NULL)
		{
			delete entry.m_image;
			entry.m_image = NULL;
		}
		//entry.m_image =  new Aria::Image;
		//entry.m_image->Load(Aria::Path::Combine(m_location, entry.m_fileName), Aria::ImageFormat_Jpeg);
		entry.m_image = CGThumbnail::Make(Aria::Path::Combine(m_location, entry.m_fileName), maxSize);
		entry.m_imageLoaded = true;
		entry.m_locked = false;
		SendMessage(WM_ENTRYCHANGED, 0, 0);
	}
private:
	HIMAGELIST m_imageList;
	Aria::String m_location;
	ptr_vector<CGThumbEntry> m_entries;
	thread_group m_threads;
};